{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit L01_modPlayerUtils;

interface

uses Classes, modClasses,
     modPlanete,
     modFlotteVaisseauxChoix,

     modGidToStr,

     
     modBaseType;



const
   CNT_DEPL_RESSOURCE_NO_VAISSEAUX = 5;
   CNT_DEPL_RESSOURCE_NO_PT_NOR_GT = 6;
   CNT_DEPL_RESSOURCE_FRET_TROP_PETIT = 7;
   CNT_DEPL_RESSOURCE_NO_VAISSEAUX_OPT = 8;

   
type

    // metal, cristal, deuterium  <-
    // metal, deuterium, cristal      <- x
    // cristal, metal, deuterium  <- y
    // cristal, deuterium, metal  <-
    // deuterium, metal, cristal      <- x
    // deuterium, cristal, metal  <-

    TRemplissageMethod = (rmMetalCristalDeuterium,
                          rmCristalDeuteriumMetal,
                          rmDeuteriumCristalMetal,

                          rmCristalMetalDeuterium,

                          rmMetalDeuteriumCristal,
                          rmDeuteriumMetalCristal);


                          
     TPlayerUtils = class(TPlayerGestion)
     private
        procedure ClearOwn;


     public
        // Methode de remplissage des vaisseaux
        MethodRemplissageVaisseaux         : TRemplissageMethod;


        // Procedure permettant d'obtenir la capacite de fret requise pour prendre le maximum de ressource
        ProcForCapacite: TProcGetFretCapaciteRequise;


        // renvoi la capacité de fret pour capturer les ressources
        function GetCapaciteDeFret(Res: TOGRessourceAll) :integer;


        // Déplace les ressources de la planète actuelle (fonction pour minier)
        function DeplacementRessource(Destination: TPlanetLocation; EnvoiMemeSiPasAssezDeCapaciteDeFret: boolean; bUseGT, bUsePT, bPreferencePT: boolean; SpeedEnvoi: integer; bOrdreStationner: boolean): integer;


        // En fonction de la méthode de remplissage choisi, on détermine les ressources
        // à transporter de la planète actuelle
        function GetRessourceAEvacuer(Planete: TPlaneteInfo; CapaciteVaisseaux: integer): TOGRessourceAll;


        procedure Clear(bDelFichierSave: boolean); override;
        constructor Create;
     end;


implementation

uses Math, modFormulesDuJeu;

procedure TPlayerUtils.Clear(bDelFichierSave: boolean);
begin
    inherited Clear(bDelFichierSave);

    ClearOwn;
end;

procedure TPlayerUtils.ClearOwn;
begin
    // pas besoin de sécurité car ces éléments sont déjà créé lors du Create
    MethodRemplissageVaisseaux := rmDeuteriumCristalMetal;
    ProcForCapacite := mfdgGetFretCapaciteRequiseContrainteSM;
end;

constructor TPlayerUtils.Create;
begin
    inherited Create;

    ClearOwn;
end;

function TPlayerUtils.GetCapaciteDeFret(Res: TOGRessourceAll) :integer;
begin
   if Assigned(ProcForCapacite) then begin
      Result := ProcForCapacite(Res);
   end else begin
      Result := mfdgGetFretCapaciteRequise(Res);
   end;


end;


function TPlayerUtils.DeplacementRessource(Destination: TPlanetLocation; EnvoiMemeSiPasAssezDeCapaciteDeFret: boolean; bUseGT, bUsePT, bPreferencePT: boolean; SpeedEnvoi: integer; bOrdreStationner: boolean): integer;
label lDoGT, lDoPT;
var r, i: integer;
    RessourceToEnvoi: TOGRessourceAll;
    TotalRessource: int64;
    peVaisseau: PVaisseauSelInfos;
    peVaisseauPT, peVaisseauGT: PVaisseauSelInfos;

    CapaciteDeFretGT: int64;
    CapaciteDeFretPT: int64;
    CapaciteDeFretAutre: int64;

    CapaciteDeFretUsed: int64;
    
    NbToUse: integer;

    InfosEnvoi: TFlotteEnvoiData;

    rFlotte: TFlotteEnvoiRes;
begin

    r := RetreiveFlotte1Page;

    if r = CNT_ERROR_SOCKET then begin
       DoSleep(1200);

       r := RetreiveFlotte1Page;
       if r < 0 then begin
          Result := r;
          exit;
       end;

    end else if r < 0 then begin
       // autre erreur
       Result := r;
       exit;
    end;

    Result := -1;


    if cFlotteVaisseaux.VaisseauxList.Count < 1 then begin
       // pas de vaisseaux
       Result := CNT_DEPL_RESSOURCE_NO_VAISSEAUX;
       exit;
    end;

    CapaciteDeFretPT := 0;
    CapaciteDeFretGT := 0;
    CapaciteDeFretAutre := 0;

    peVaisseauPT := nil;
    peVaisseauGT := nil;

    TotalRessource := curPlanete.FLastRessources.Metal + curPlanete.FLastRessources.Cristal + curPlanete.FLastRessources.Deuterium;

    for i := 0 to cFlotteVaisseaux.VaisseauxList.Count - 1 do begin
       peVaisseau := cFlotteVaisseaux.VaisseauxList.Items[i];

       if peVaisseau.GID = CNT_GID_VAISSEAUX_GRAND_TRANSPORTEUR then begin
          CapaciteDeFretGT := peVaisseau.Capacite * peVaisseau.Max;
          peVaisseauGT := peVaisseau;

       end else if peVaisseau.GID = CNT_GID_VAISSEAUX_PETIT_TRANSPORTEUR then begin
          CapaciteDeFretPT := peVaisseau.Capacite * peVaisseau.Max;
          peVaisseauPT := peVaisseau;
       end;

    end;

    if (peVaisseauPT = nil) and (peVaisseauGT = nil) then begin
       // ni de PT, ni de GT sur la planete
       Result := CNT_DEPL_RESSOURCE_NO_PT_NOR_GT;
       exit;

    end else if peVaisseauGT = nil then begin
       // que des PT
       if bUsePT = false then begin
          // on ne doit pas utiliser les GT
          Result := CNT_DEPL_RESSOURCE_NO_VAISSEAUX_OPT;
          exit;
       end;
       goto lDoPT;

    end else if peVaisseauPT = nil then begin
       // que des GT
       if bUseGT = false then begin
          // on ne doit utiliser que des PT
          Result := CNT_DEPL_RESSOURCE_NO_VAISSEAUX_OPT;
          exit;
       end;
       goto lDoGT;
    end;

    if bUseGT and bUsePT then begin

       if bPreferencePT then begin
          if CapaciteDeFretPT > TotalRessource then begin
             // on utilisera que les PT
             goto lDoPT;

          end else begin
             // il faudra aussi des GT
             CapaciteDeFretUsed := CapaciteDeFretPT + CapaciteDeFretGT;
          end;

       end else begin
          if CapaciteDeFretGT > TotalRessource then begin
             // on utilisera que les GT
             goto lDoGT;

          end else begin
             // il faudra aussi des PT
             CapaciteDeFretUsed := CapaciteDeFretGT + CapaciteDeFretPT;
          end;

       end;

       // si on arrive ici, c'est que l'on utilise et les GT, et les PT

       if CapaciteDeFretUsed > TotalRessource then begin
          // on arrive à tout remplir en combinant les deux

          // on commence par les GT
          if CapaciteDeFretGT > TotalRessource then begin
             // seuls les GT suffisent
             // on a du alors choisir la priorité au PT
             goto lDoGT;
          end;

          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_GRAND_TRANSPORTEUR, peVaisseauGT.Max);

          NbToUse := Ceil((TotalRessource - cFlotteVaisseaux.VaisseauxList.GetCapacitePourVaisseauxSel) / peVaisseauPT.Capacite);
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_PETIT_TRANSPORTEUR, NbToUse);


       end else begin
          // on arrive pas à tout remplir en utilisant les deux type de vaisseaux
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_GRAND_TRANSPORTEUR, peVaisseauGT.Max);
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_PETIT_TRANSPORTEUR, peVaisseauPT.Max);
       end;


    end else if bUseGT then begin
lDoGT:
       if CapaciteDeFretGT > TotalRessource then begin
          // on a suffisament de GT pour tout transporter
          // Etant donne que l'on transporte tout, le deuterium utilise comme carburant est déjà compris
          NbToUse := Ceil(TotalRessource / peVaisseauGT.Capacite);
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_GRAND_TRANSPORTEUR, NbToUse);

       end else begin
          // pas assez de GT, il les faudra tous
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_GRAND_TRANSPORTEUR, peVaisseauGT.Max);
       end;

    end else if bUsePT then begin
lDoPT:
       if CapaciteDeFretPT > TotalRessource then begin
          // on a suffisament de PT pour tout transporter
          // Etant donne que l'on transporte tout, le deuterium utilise comme carburant est déjà compris
          NbToUse := Ceil(TotalRessource / peVaisseauPT.Capacite);
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_PETIT_TRANSPORTEUR, NbToUse);

       end else begin
          // pas assez de PT, il les faudra tous
          cFlotteVaisseaux.VaisseauxList.SetVaisseauxAUtiliser(CNT_GID_VAISSEAUX_PETIT_TRANSPORTEUR, peVaisseauPT.Max);
       end;

    end else begin
       // rien d'activé...
       exit;
    end;

    CapaciteDeFretUsed := cFlotteVaisseaux.VaisseauxList.GetCapacitePourVaisseauxSel;

    if CapaciteDeFretUsed < 10 then begin
       // erreur
       DoDebug('DeplacementRessource - Capacite de fret invalide');
       exit;
    end;

    if NoT(EnvoiMemeSiPasAssezDeCapaciteDeFret) and (CapaciteDeFretUsed < TotalRessource) then begin
       // on n'envoi pas
       Result := CNT_DEPL_RESSOURCE_FRET_TROP_PETIT;
       exit;
    end;

    RessourceToEnvoi := GetRessourceAEvacuer(curPlanete, CapaciteDeFretUsed);



    InfosEnvoi.Cible.Galaxie       := Destination.Galaxie;
    InfosEnvoi.Cible.SystemSolaire := Destination.SystemSolaire;
    if mbtIsLune(Destination) then begin
       InfosEnvoi.Cible.Position := mbtGetRealPosition(Destination.Position);
       InfosEnvoi.CibleType      := ctLune;
    end else begin
       InfosEnvoi.Cible.Position := Destination.Position;
       InfosEnvoi.CibleType      := ctPlanete;
    end;

    InfosEnvoi.Vitesse := SpeedEnvoi;

    if bOrdreStationner then begin
       InfosEnvoi.Ordre   := ooBaser;
    end else begin
       InfosEnvoi.Ordre   := ooTransport;
    end;

    InfosEnvoi.Ressources := RessourceToEnvoi;


    rFlotte := ProceedEnvoiDeFlotte(@InfosEnvoi);

    Result := CNT_ERROR_FATAL;

    case rFlotte of
       //ferFatalError : ;
       ferSocketError: Result := CNT_ERROR_SOCKET;
       //ferShouldRetry: ;
       //ferShouldRetryButNotError: ;
       ferSuccess: Result := CNT_SUCCESS;
       //ferNoVaisseaux: ;
       //ferFlotteMaxAtteinte: ;
       ferNotOrReLoginCertain: Result := CNT_ERROR_MUST_RELOGIN;
       ferPlaneteInvalid: Result := CNT_ERROR_USER;
    end;

end;



// Sert pour l'évacuation de flotte, mais aussi pour le transport massif de ressources
function TPlayerUtils.GetRessourceAEvacuer(Planete: TPlaneteInfo; CapaciteVaisseaux: integer): TOGRessourceAll;
var TotalRessource: int64;
begin

    TotalRessource := Planete.FLastRessources.Metal + Planete.FLastRessources.Cristal + Planete.FLastRessources.Deuterium;

    Result.Metal     := 0;
    Result.Cristal   := 0;
    Result.Deuterium := 0;

    if TotalRessource <= CapaciteVaisseaux then begin
       // on peut tout evacuer
       Result.Metal      := Planete.FLastRessources.Metal;
       Result.Cristal    := Planete.FLastRessources.Cristal;
       Result.Deuterium  := Planete.FLastRessources.Deuterium;
       exit;
    end;

    // sinon il faut utiliser un ordre de remplissage

    case MethodRemplissageVaisseaux of
        rmMetalCristalDeuterium: begin
            // ceci etant l'ordre par défaut d'ogame, on met tout au maximum
            Result.Metal      := Planete.FLastRessources.Metal;
            Result.Cristal    := Planete.FLastRessources.Cristal;
            Result.Deuterium  := Planete.FLastRessources.Deuterium;
        end;


        rmCristalMetalDeuterium: begin
            // on rempli de Cristal
            Result.Cristal := Planete.FLastRessources.Cristal;

            if Planete.FLastRessources.Cristal >= CapaciteVaisseaux then begin
               // plus de place pour d'autre ressource
               // ogame s'occupe lui meme de remplir avec les ressources possible
               exit;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Cristal;



            // verifie si on peut mettre toute la quantite de la ressource suivante
            if Planete.FLastRessources.Metal >= CapaciteVaisseaux then begin
               // ce n'est pas le cas
               Result.Metal  := CapaciteVaisseaux;
               exit;

            end else begin
               // c'est le cas
               Result.Metal  := Planete.FLastRessources.Metal;
            end;


            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Metal;

            // Etant donné que le Metal et Cristal sont au maximum, on peut
            // mettre le Deuterium au maximum egalement car le Metal,Cristal sont
            // prioritaire sur le Deuterium
            Result.Deuterium  := Planete.FLastRessources.Deuterium;
        end;

        rmCristalDeuteriumMetal: begin
            // on rempli de Cristal
            Result.Cristal := Planete.FLastRessources.Cristal;

            if Planete.FLastRessources.Cristal >= CapaciteVaisseaux then begin
               // plus de place pour d'autre ressource
               exit;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Cristal;

            // verifie si on peut mettre toute la quantite de la ressource suivante
            if Planete.FLastRessources.Deuterium >= CapaciteVaisseaux then begin
               // ce n'est pas le cas
               // on utilise ici encore, le fait qu'ogame prenne en priorité le Cristal
               // sur le deuterium, on met alors le deuterium au maximum
               Result.Deuterium  := Planete.FLastRessources.Deuterium;
               exit;

            end else begin
               // c'est le cas
               Result.Deuterium  := Planete.FLastRessources.Deuterium;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Deuterium;

            if Planete.FLastRessources.Metal >= CapaciteVaisseaux then begin
               Result.Metal := CapaciteVaisseaux;
               exit;
            end;

            // on peut mettre tout le metal... etrange
            Result.Metal := Planete.FLastRessources.Metal;

        end;
        
        rmDeuteriumCristalMetal: begin
            // on rempli de Deuterium
            Result.Deuterium := Planete.FLastRessources.Deuterium;

            if Planete.FLastRessources.Deuterium >= CapaciteVaisseaux then begin
               // plus de place pour d'autre ressource
               exit;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Deuterium;

            // verifie si on peut mettre toute la quantite de la ressource suivante
            if Planete.FLastRessources.Cristal >= CapaciteVaisseaux then begin
               // ce n'est pas le cas
               Result.Cristal  := CapaciteVaisseaux;
               exit;

            end else begin
               // c'est le cas sinon
               Result.Cristal  := Planete.FLastRessources.Cristal;;
            end;


            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Cristal;

            if Planete.FLastRessources.Metal >= CapaciteVaisseaux then begin
               Result.Metal := CapaciteVaisseaux;
               exit;
            end;

            // on peut mettre tout le metal... etrange
            Result.Metal := Planete.FLastRessources.Metal;

        end;

        rmMetalDeuteriumCristal: begin
            // on rempli de métal
            Result.Metal := Planete.FLastRessources.Metal;

            if Planete.FLastRessources.Metal >= CapaciteVaisseaux then begin
               // plus de place pour d'autre ressource
               // ogame s'occupe lui meme de remplir avec les ressources possible
               Result.Deuterium := Planete.FLastRessources.Deuterium;
               exit;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Metal;



            // verifie si on peut mettre toute la quantite de la ressource suivante
            if Planete.FLastRessources.Deuterium >= CapaciteVaisseaux then begin
               // ce n'est pas le cas
               Result.Deuterium  := Planete.FLastRessources.Deuterium;
               exit;

            end else begin
               // c'est le cas sinon
               Result.Deuterium  := Planete.FLastRessources.Deuterium;;
            end;



            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Deuterium;

            if Planete.FLastRessources.Cristal >= CapaciteVaisseaux then begin
               Result.Cristal := CapaciteVaisseaux;
               exit;
            end;

            // on peut mettre tout le cristal... etrange
            Result.Cristal := Planete.FLastRessources.Cristal;

        end;

        
        rmDeuteriumMetalCristal: begin
            // on rempli de Deuterium
            Result.Deuterium := Planete.FLastRessources.Deuterium;

            if Planete.FLastRessources.Deuterium >= CapaciteVaisseaux then begin
               // plus de place pour d'autre ressource
               exit;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Deuterium;


            // verifie si on peut mettre toute la quantite de la ressource suivante
            if Planete.FLastRessources.Metal >= CapaciteVaisseaux then begin
               // ce n'est pas le cas
               Result.Metal  := CapaciteVaisseaux;
               exit;

            end else begin
               // c'est le cas sinon
               Result.Metal  := Planete.FLastRessources.Metal;;
            end;

            // on calcul la quantite restante de ressource transportable
            CapaciteVaisseaux := CapaciteVaisseaux - Planete.FLastRessources.Metal;

            if Planete.FLastRessources.Cristal >= CapaciteVaisseaux then begin
               Result.Cristal := CapaciteVaisseaux;
               exit;
            end;

            // on peut mettre tout le cristal... etrange
            Result.Cristal := Planete.FLastRessources.Cristal;

        end;



    end;

end;


end.
